﻿using Microsoft.Extensions.Options;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;

namespace Xms.Abp.Core;

public class DefaultSerialNumberProvider: ISerialNumberProvider, ISingletonDependency
{
    public string Name => "Default";
    protected string SavePath { get; }
    protected readonly ConcurrentDictionary<string, object> Lockers = new();

    public DefaultSerialNumberProvider(IOptions<DefaultSerialNumberOptions> optionsAccessor)
    {
        SavePath = optionsAccessor.Value.SavePath ?? Path.Combine(AppContext.BaseDirectory, "SerialNumbers");
    }

    protected object GetLocker(string key)
    {
        return Lockers.GetOrAdd(key, () => new object());
    }

    public async Task<long> CreateAsync(string key, int quantity = 1, SerialNumberGrowthType growthType = SerialNumberGrowthType.None, CancellationToken cancellationToken = default)
    {
        var filePath = Path.Combine(SavePath, key + ".txt");
        await Task.CompletedTask;
        lock (GetLocker(key))
        {
            var value = 0L;
            if (File.Exists(filePath))
            {
                var v = long.Parse(File.ReadAllText(filePath).Trim());
                if (growthType == SerialNumberGrowthType.None)
                {
                    value = v;
                }
                else
                {
                    var lastWriteTime = File.GetLastWriteTime(filePath);
                    value = growthType switch
                    {
                        SerialNumberGrowthType.ByYear => lastWriteTime.Year != DateTime.Today.Year ? value : v,
                        SerialNumberGrowthType.ByMonth => lastWriteTime.Year != DateTime.Today.Year || lastWriteTime.Month != DateTime.Today.Month ? value : v,
                        SerialNumberGrowthType.ByDay => lastWriteTime.Date != DateTime.Today ? value : v,
                        SerialNumberGrowthType.ByWeek => (long)TimeSpan.FromTicks(lastWriteTime.Ticks).TotalDays / 7 != (long)TimeSpan.FromTicks(DateTime.Today.Ticks).TotalDays / 7 ? value : v,
                        SerialNumberGrowthType.ByHour => lastWriteTime.Date != DateTime.Today || lastWriteTime.Hour != DateTime.Now.Hour ? value : v,
                        SerialNumberGrowthType.ByMinute => lastWriteTime.Date != DateTime.Today || lastWriteTime.Hour != DateTime.Now.Hour || lastWriteTime.Minute != DateTime.Now.Minute ? value : v,
                        _ => v,
                    };
                }
            }
            value += quantity;
            File.WriteAllText(filePath, value.ToString());
            return value;
        }
    }
}
